﻿using System;

public sealed class CPU
{
    private int add;
    private int addr;
    private int addrMode;
    private bool asApplet = Globals.appletMode;
    public bool crash;
    private int cycleAdd;
    private int cycleCount;
    public int cyclesToHalt;
    private bool emulateSound = Globals.enableSound;
    public bool Emulating = true;
    private int F_BRK;
    private int F_BRK_NEW;
    private int F_CARRY;
    private int F_CARRY_NEW;
    private int F_DECIMAL;
    private int F_DECIMAL_NEW;
    private int F_INTERRUPT;
    private int F_INTERRUPT_NEW;
    private int F_NOTUSED;
    private int F_NOTUSED_NEW;
    private int F_OVERFLOW;
    private int F_OVERFLOW_NEW;
    private int F_SIGN;
    private int F_SIGN_NEW;
    private int F_ZERO;
    private int F_ZERO_NEW;
    public const int IRQ_NMI = 1;
    public const int IRQ_NORMAL = 0;
    public const int IRQ_RESET = 2;
    public bool irqRequested;
    private int irqType;
    public bool loading;
    private short[] mem;
    private MemoryMapper mmap;
    private NES nes;
    private int opaddr;
    private int[] opdata;
    private int opinf;
    private int palCnt;
    private bool palEmu = Globals.palEmulation;
    private PAPU papu;
    private PPU ppu;
    private int REG_ACC;
    public int REG_ACC_NEW;
    private int REG_PC;
    public int REG_PC_NEW;
    public int REG_SP;
    private int REG_STATUS;
    public int REG_STATUS_NEW;
    private int REG_X;
    public int REG_X_NEW;
    private int REG_Y;
    public int REG_Y_NEW;
    public bool saving;
    public bool stopRunning;
    private int temp;

    public CPU(NES nes)
    {
        this.nes = nes;
    }

    public void destroy()
    {
        this.nes = null;
        this.mmap = null;
    }

    private void doIrq(int status)
    {
        this.REG_PC_NEW++;
        this.push((this.REG_PC_NEW >> 8) & 0xff);
        this.push(this.REG_PC_NEW & 0xff);
        this.push(status);
        this.F_INTERRUPT_NEW = 1;
        this.F_BRK_NEW = 0;
        this.REG_PC_NEW = this.mmap.load(0xfffe) | (this.mmap.load(0xffff) << 8);
        this.REG_PC_NEW--;
    }

    private void doNonMaskableInterrupt(int status)
    {
        if ((this.mmap.load(0x2000) & 0x80) != 0)
        {
            this.REG_PC_NEW++;
            this.push((this.REG_PC_NEW >> 8) & 0xff);
            this.push(this.REG_PC_NEW & 0xff);
            this.push(status);
            this.REG_PC_NEW = this.mmap.load(0xfffa) | (this.mmap.load(0xfffb) << 8);
            this.REG_PC_NEW--;
        }
    }

    private void doResetInterrupt()
    {
        this.REG_PC_NEW = this.mmap.load(0xfffc) | (this.mmap.load(0xfffd) << 8);
        this.REG_PC_NEW--;
    }

    public void emulate()
    {
        this.Emulating = true;
        while (this.Emulating)
        {
            if (this.stopRunning)
            {
                break;
            }
            if (this.irqRequested)
            {
                this.temp = ((((((this.F_CARRY | (((this.F_ZERO == null) ? 1 : 0) << 1)) | (this.F_INTERRUPT << 2)) | (this.F_DECIMAL << 3)) | (this.F_BRK << 4)) | (this.F_NOTUSED << 5)) | (this.F_OVERFLOW << 6)) | (this.F_SIGN << 7);
                this.REG_PC_NEW = this.REG_PC;
                this.F_INTERRUPT_NEW = this.F_INTERRUPT;
                switch (this.irqType)
                {
                    case 0:
                        if (this.F_INTERRUPT == 0)
                        {
                            this.doIrq(this.temp);
                        }
                        break;

                    case 1:
                        this.doNonMaskableInterrupt(this.temp);
                        break;

                    case 2:
                        this.doResetInterrupt();
                        break;
                }
                this.REG_PC = this.REG_PC_NEW;
                this.F_INTERRUPT = this.F_INTERRUPT_NEW;
                this.F_BRK = this.F_BRK_NEW;
                this.irqRequested = false;
            }
            this.opinf = this.opdata[this.mmap.load(this.REG_PC + 1)];
            this.cycleCount = this.opinf >> 0x18;
            this.cycleAdd = 0;
            this.addrMode = (this.opinf >> 8) & 0xff;
            this.opaddr = this.REG_PC;
            this.REG_PC += (this.opinf >> 0x10) & 0xff;
            switch (this.addrMode)
            {
                case 0:
                    this.addr = this.load(this.opaddr + 2);
                    goto Label_04CB;

                case 1:
                    this.addr = this.load(this.opaddr + 2);
                    if (this.addr >= 0x80)
                    {
                        break;
                    }
                    this.addr += this.REG_PC;
                    goto Label_04CB;

                case 3:
                    this.addr = this.load16bit(this.opaddr + 2);
                    goto Label_04CB;

                case 4:
                    this.addr = this.REG_ACC;
                    goto Label_04CB;

                case 5:
                    this.addr = this.REG_PC;
                    goto Label_04CB;

                case 6:
                    this.addr = (this.load(this.opaddr + 2) + this.REG_X) & 0xff;
                    goto Label_04CB;

                case 7:
                    this.addr = (this.load(this.opaddr + 2) + this.REG_Y) & 0xff;
                    goto Label_04CB;

                case 8:
                    this.addr = this.load16bit(this.opaddr + 2);
                    if ((this.addr & 0xff00) != ((this.addr + this.REG_X) & 0xff00))
                    {
                        this.cycleAdd = 1;
                    }
                    this.addr += this.REG_X;
                    goto Label_04CB;

                case 9:
                    this.addr = this.load16bit(this.opaddr + 2);
                    if ((this.addr & 0xff00) != ((this.addr + this.REG_Y) & 0xff00))
                    {
                        this.cycleAdd = 1;
                    }
                    this.addr += this.REG_Y;
                    goto Label_04CB;

                case 10:
                    this.addr = this.load(this.opaddr + 2);
                    if ((this.addr & 0xff00) != ((this.addr + this.REG_X) & 0xff00))
                    {
                        this.cycleAdd = 1;
                    }
                    this.addr += this.REG_X;
                    this.addr &= 0xff;
                    this.addr = this.load16bit(this.addr);
                    goto Label_04CB;

                case 11:
                    this.addr = this.load16bit(this.load(this.opaddr + 2));
                    if ((this.addr & 0xff00) != ((this.addr + this.REG_Y) & 0xff00))
                    {
                        this.cycleAdd = 1;
                    }
                    this.addr += this.REG_Y;
                    goto Label_04CB;

                case 12:
                    this.addr = this.load16bit(this.opaddr + 2);
                    if (this.addr >= 0x1fff)
                    {
                        goto Label_0485;
                    }
                    this.addr = this.mem[this.addr] + (this.mem[(this.addr & 0xff00) | (((this.addr & 0xff) + 1) & 0xff)] << 8);
                    goto Label_04CB;

                default:
                    goto Label_04CB;
            }
            this.addr += this.REG_PC - 0x100;
            goto Label_04CB;
        Label_0485:
            this.addr = this.mmap.load(this.addr) + (this.mmap.load((this.addr & 0xff00) | (((this.addr & 0xff) + 1) & 0xff)) << 8);
        Label_04CB:
            this.addr &= 0xffff;
            switch ((this.opinf & 0xff))
            {
                case 0:
                    this.temp = (this.REG_ACC + this.load(this.addr)) + this.F_CARRY;
                    this.F_OVERFLOW = ((((this.REG_ACC ^ this.load(this.addr)) & 0x80) == 0) && (((this.REG_ACC ^ this.temp) & 0x80) != 0)) ? 1 : 0;
                    this.F_CARRY = (this.temp > 0xff) ? 1 : 0;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp & 0xff;
                    this.REG_ACC = this.temp & 0xff;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 1:
                    this.REG_ACC &= this.load(this.addr);
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    if (this.addrMode != 11)
                    {
                        this.cycleCount += this.cycleAdd;
                    }
                    goto Label_162C;

                case 2:
                    if (this.addrMode != 4)
                    {
                        break;
                    }
                    this.F_CARRY = (this.REG_ACC >> 7) & 1;
                    this.REG_ACC = (this.REG_ACC << 1) & 0xff;
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    goto Label_162C;

                case 3:
                    if (this.F_CARRY == 0)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 4:
                    if (this.F_CARRY == 1)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 5:
                    if (this.F_ZERO == 0)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 6:
                    this.temp = this.load(this.addr);
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_OVERFLOW = (this.temp >> 6) & 1;
                    this.temp &= this.REG_ACC;
                    this.F_ZERO = this.temp;
                    goto Label_162C;

                case 7:
                    if (this.F_SIGN == 1)
                    {
                        this.cycleCount++;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 8:
                    if (this.F_ZERO != 0)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 9:
                    if (this.F_SIGN == 0)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 10:
                    this.REG_PC += 2;
                    this.push((this.REG_PC >> 8) & 0xff);
                    this.push(this.REG_PC & 0xff);
                    this.F_BRK = 1;
                    this.push(((((((this.F_CARRY | (((this.F_ZERO == null) ? 1 : 0) << 1)) | (this.F_INTERRUPT << 2)) | (this.F_DECIMAL << 3)) | (this.F_BRK << 4)) | (this.F_NOTUSED << 5)) | (this.F_OVERFLOW << 6)) | (this.F_SIGN << 7));
                    this.F_INTERRUPT = 1;
                    this.REG_PC = this.load16bit(0xfffe);
                    this.REG_PC--;
                    goto Label_162C;

                case 11:
                    if (this.F_OVERFLOW == 0)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 12:
                    if (this.F_OVERFLOW == 1)
                    {
                        this.cycleCount += ((this.opaddr & 0xff00) != (this.addr & 0xff00)) ? 2 : 1;
                        this.REG_PC = this.addr;
                    }
                    goto Label_162C;

                case 13:
                    this.F_CARRY = 0;
                    goto Label_162C;

                case 14:
                    this.F_DECIMAL = 0;
                    goto Label_162C;

                case 15:
                    this.F_INTERRUPT = 0;
                    goto Label_162C;

                case 0x10:
                    this.F_OVERFLOW = 0;
                    goto Label_162C;

                case 0x11:
                    this.temp = this.REG_ACC - this.load(this.addr);
                    this.F_CARRY = (this.temp >= 0) ? 1 : 0;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp & 0xff;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 0x12:
                    this.temp = this.REG_X - this.load(this.addr);
                    this.F_CARRY = (this.temp >= 0) ? 1 : 0;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp & 0xff;
                    goto Label_162C;

                case 0x13:
                    this.temp = this.REG_Y - this.load(this.addr);
                    this.F_CARRY = (this.temp >= 0) ? 1 : 0;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp & 0xff;
                    goto Label_162C;

                case 20:
                    this.temp = (this.load(this.addr) - 1) & 0xff;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp;
                    this.write(this.addr, (short) this.temp);
                    goto Label_162C;

                case 0x15:
                    this.REG_X = (this.REG_X - 1) & 0xff;
                    this.F_SIGN = (this.REG_X >> 7) & 1;
                    this.F_ZERO = this.REG_X;
                    goto Label_162C;

                case 0x16:
                    this.REG_Y = (this.REG_Y - 1) & 0xff;
                    this.F_SIGN = (this.REG_Y >> 7) & 1;
                    this.F_ZERO = this.REG_Y;
                    goto Label_162C;

                case 0x17:
                    this.REG_ACC = (this.load(this.addr) ^ this.REG_ACC) & 0xff;
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 0x18:
                    this.temp = (this.load(this.addr) + 1) & 0xff;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp;
                    this.write(this.addr, (short) (this.temp & 0xff));
                    goto Label_162C;

                case 0x19:
                    this.REG_X = (this.REG_X + 1) & 0xff;
                    this.F_SIGN = (this.REG_X >> 7) & 1;
                    this.F_ZERO = this.REG_X;
                    goto Label_162C;

                case 0x1a:
                    this.REG_Y++;
                    this.REG_Y &= 0xff;
                    this.F_SIGN = (this.REG_Y >> 7) & 1;
                    this.F_ZERO = this.REG_Y;
                    goto Label_162C;

                case 0x1b:
                    this.REG_PC = this.addr - 1;
                    goto Label_162C;

                case 0x1c:
                    this.push((this.REG_PC >> 8) & 0xff);
                    this.push(this.REG_PC & 0xff);
                    this.REG_PC = this.addr - 1;
                    goto Label_162C;

                case 0x1d:
                    this.REG_ACC = this.load(this.addr);
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 30:
                    this.REG_X = this.load(this.addr);
                    this.F_SIGN = (this.REG_X >> 7) & 1;
                    this.F_ZERO = this.REG_X;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 0x1f:
                    this.REG_Y = this.load(this.addr);
                    this.F_SIGN = (this.REG_Y >> 7) & 1;
                    this.F_ZERO = this.REG_Y;
                    this.cycleCount += this.cycleAdd;
                    goto Label_162C;

                case 0x20:
                    if (this.addrMode != 4)
                    {
                        goto Label_0F43;
                    }
                    this.temp = this.REG_ACC & 0xff;
                    this.F_CARRY = this.temp & 1;
                    this.temp = this.temp >> 1;
                    this.REG_ACC = this.temp;
                    goto Label_0F8A;

                case 0x21:
                    goto Label_162C;

                case 0x22:
                    this.temp = (this.load(this.addr) | this.REG_ACC) & 0xff;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp;
                    this.REG_ACC = this.temp;
                    if (this.addrMode != 11)
                    {
                        this.cycleCount += this.cycleAdd;
                    }
                    goto Label_162C;

                case 0x23:
                    this.push(this.REG_ACC);
                    goto Label_162C;

                case 0x24:
                    this.F_BRK = 1;
                    this.push(((((((this.F_CARRY | (((this.F_ZERO == null) ? 1 : 0) << 1)) | (this.F_INTERRUPT << 2)) | (this.F_DECIMAL << 3)) | (this.F_BRK << 4)) | (this.F_NOTUSED << 5)) | (this.F_OVERFLOW << 6)) | (this.F_SIGN << 7));
                    goto Label_162C;

                case 0x25:
                    this.REG_ACC = this.pull();
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    goto Label_162C;

                case 0x26:
                    this.temp = this.pull();
                    this.F_CARRY = this.temp & 1;
                    this.F_ZERO = (((this.temp >> 1) & 1) == 1) ? 0 : 1;
                    this.F_INTERRUPT = (this.temp >> 2) & 1;
                    this.F_DECIMAL = (this.temp >> 3) & 1;
                    this.F_BRK = (this.temp >> 4) & 1;
                    this.F_NOTUSED = (this.temp >> 5) & 1;
                    this.F_OVERFLOW = (this.temp >> 6) & 1;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_NOTUSED = 1;
                    goto Label_162C;

                case 0x27:
                    if (this.addrMode != 4)
                    {
                        goto Label_11A0;
                    }
                    this.temp = this.REG_ACC;
                    this.add = this.F_CARRY;
                    this.F_CARRY = (this.temp >> 7) & 1;
                    this.temp = ((this.temp << 1) & 0xff) + this.add;
                    this.REG_ACC = this.temp;
                    goto Label_11FC;

                case 40:
                    if (this.addrMode != 4)
                    {
                        goto Label_1265;
                    }
                    this.add = this.F_CARRY << 7;
                    this.F_CARRY = this.REG_ACC & 1;
                    this.temp = (this.REG_ACC >> 1) + this.add;
                    this.REG_ACC = this.temp;
                    goto Label_12BB;

                case 0x29:
                    this.temp = this.pull();
                    this.F_CARRY = this.temp & 1;
                    this.F_ZERO = (((this.temp >> 1) & 1) == 0) ? 1 : 0;
                    this.F_INTERRUPT = (this.temp >> 2) & 1;
                    this.F_DECIMAL = (this.temp >> 3) & 1;
                    this.F_BRK = (this.temp >> 4) & 1;
                    this.F_NOTUSED = (this.temp >> 5) & 1;
                    this.F_OVERFLOW = (this.temp >> 6) & 1;
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.REG_PC = this.pull();
                    this.REG_PC += this.pull() << 8;
                    if (this.REG_PC != 0xffff)
                    {
                        goto Label_139B;
                    }
                    return;

                case 0x2a:
                    this.REG_PC = this.pull();
                    this.REG_PC += this.pull() << 8;
                    if (this.REG_PC != 0xffff)
                    {
                        goto Label_162C;
                    }
                    return;

                case 0x2b:
                    this.temp = (this.REG_ACC - this.load(this.addr)) - (1 - this.F_CARRY);
                    this.F_SIGN = (this.temp >> 7) & 1;
                    this.F_ZERO = this.temp & 0xff;
                    this.F_OVERFLOW = ((((this.REG_ACC ^ this.temp) & 0x80) != 0) && (((this.REG_ACC ^ this.load(this.addr)) & 0x80) != 0)) ? 1 : 0;
                    this.F_CARRY = (this.temp < 0) ? 0 : 1;
                    this.REG_ACC = this.temp & 0xff;
                    if (this.addrMode != 11)
                    {
                        this.cycleCount += this.cycleAdd;
                    }
                    goto Label_162C;

                case 0x2c:
                    this.F_CARRY = 1;
                    goto Label_162C;

                case 0x2d:
                    this.F_DECIMAL = 1;
                    goto Label_162C;

                case 0x2e:
                    this.F_INTERRUPT = 1;
                    goto Label_162C;

                case 0x2f:
                    this.write(this.addr, (short) this.REG_ACC);
                    goto Label_162C;

                case 0x30:
                    this.write(this.addr, (short) this.REG_X);
                    goto Label_162C;

                case 0x31:
                    this.write(this.addr, (short) this.REG_Y);
                    goto Label_162C;

                case 50:
                    this.REG_X = this.REG_ACC;
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    goto Label_162C;

                case 0x33:
                    this.REG_Y = this.REG_ACC;
                    this.F_SIGN = (this.REG_ACC >> 7) & 1;
                    this.F_ZERO = this.REG_ACC;
                    goto Label_162C;

                case 0x34:
                    this.REG_X = this.REG_SP - 0x100;
                    this.F_SIGN = (this.REG_SP >> 7) & 1;
                    this.F_ZERO = this.REG_X;
                    goto Label_162C;

                case 0x35:
                    this.REG_ACC = this.REG_X;
                    this.F_SIGN = (this.REG_X >> 7) & 1;
                    this.F_ZERO = this.REG_X;
                    goto Label_162C;

                case 0x36:
                    this.REG_SP = this.REG_X + 0x100;
                    this.stackWrap();
                    goto Label_162C;

                case 0x37:
                    this.REG_ACC = this.REG_Y;
                    this.F_SIGN = (this.REG_Y >> 7) & 1;
                    this.F_ZERO = this.REG_Y;
                    goto Label_162C;

                default:
                    if (!this.crash)
                    {
                        this.crash = true;
                        this.stopRunning = true;
                    }
                    goto Label_162C;
            }
            this.temp = this.load(this.addr);
            this.F_CARRY = (this.temp >> 7) & 1;
            this.temp = (this.temp << 1) & 0xff;
            this.F_SIGN = (this.temp >> 7) & 1;
            this.F_ZERO = this.temp;
            this.write(this.addr, (short) this.temp);
            goto Label_162C;
        Label_0F43:
            this.temp = this.load(this.addr) & 0xff;
            this.F_CARRY = this.temp & 1;
            this.temp = this.temp >> 1;
            this.write(this.addr, (short) this.temp);
        Label_0F8A:
            this.F_SIGN = 0;
            this.F_ZERO = this.temp;
            goto Label_162C;
        Label_11A0:
            this.temp = this.load(this.addr);
            this.add = this.F_CARRY;
            this.F_CARRY = (this.temp >> 7) & 1;
            this.temp = ((this.temp << 1) & 0xff) + this.add;
            this.write(this.addr, (short) this.temp);
        Label_11FC:
            this.F_SIGN = (this.temp >> 7) & 1;
            this.F_ZERO = this.temp;
            goto Label_162C;
        Label_1265:
            this.temp = this.load(this.addr);
            this.add = this.F_CARRY << 7;
            this.F_CARRY = this.temp & 1;
            this.temp = (this.temp >> 1) + this.add;
            this.write(this.addr, (short) this.temp);
        Label_12BB:
            this.F_SIGN = (this.temp >> 7) & 1;
            this.F_ZERO = this.temp;
            goto Label_162C;
        Label_139B:
            this.REG_PC--;
            this.F_NOTUSED = 1;
        Label_162C:
            if (this.palEmu)
            {
                this.palCnt++;
                if (this.palCnt == 5)
                {
                    this.palCnt = 0;
                    this.cycleCount++;
                }
            }
            if (this.asApplet)
            {
                this.ppu.cycles = this.cycleCount * 3;
                this.ppu.emulateCycles();
            }
            if (this.emulateSound)
            {
                this.papu.clockFrameCounter(this.cycleCount);
            }
        }
        if (this.saving)
        {
            this.endEmulate();
            this.nes.stateSave();
            this.saving = false;
        }
        if (this.loading)
        {
            this.nes.stateLoad();
            this.loading = false;
        }
    }

    public void endEmulate()
    {
        this.REG_ACC_NEW = this.REG_ACC;
        this.REG_X_NEW = this.REG_X;
        this.REG_Y_NEW = this.REG_Y;
        this.REG_STATUS_NEW = this.REG_STATUS;
        this.REG_PC_NEW = this.REG_PC;
        this.F_CARRY_NEW = this.F_CARRY;
        this.F_ZERO_NEW = (this.F_ZERO == 0) ? 1 : 0;
        this.F_INTERRUPT_NEW = this.F_INTERRUPT;
        this.F_DECIMAL_NEW = this.F_DECIMAL;
        this.F_BRK_NEW = this.F_BRK;
        this.F_NOTUSED_NEW = this.F_NOTUSED;
        this.F_OVERFLOW_NEW = this.F_OVERFLOW;
        this.F_SIGN_NEW = this.F_SIGN;
    }

    public void haltCycles(int cycles)
    {
        this.cyclesToHalt += cycles;
    }

    public void init()
    {
        this.opdata = CpuInfo.OpData;
        this.mmap = this.nes.MemoryMapper;
        this.crash = false;
        this.F_BRK_NEW = 1;
        this.F_NOTUSED_NEW = 1;
        this.F_INTERRUPT_NEW = 1;
        this.irqRequested = false;
    }

    public void initRun()
    {
        this.stopRunning = false;
        this.mem = this.nes.cpuMem.mem;
        this.mmap = this.nes.memMapper;
        this.ppu = this.nes.ppu;
        this.papu = this.nes.papu;
        this.REG_ACC = this.REG_ACC_NEW;
        this.REG_X = this.REG_X_NEW;
        this.REG_Y = this.REG_Y_NEW;
        this.REG_STATUS = this.REG_STATUS_NEW;
        this.REG_PC = this.REG_PC_NEW;
        this.F_CARRY = this.F_CARRY_NEW;
        this.F_ZERO = (this.F_ZERO_NEW == 0) ? 1 : 0;
        this.F_INTERRUPT = this.F_INTERRUPT_NEW;
        this.F_DECIMAL = this.F_DECIMAL_NEW;
        this.F_NOTUSED = this.F_NOTUSED_NEW;
        this.F_BRK = this.F_BRK_NEW;
        this.F_OVERFLOW = this.F_OVERFLOW_NEW;
        this.F_SIGN = this.F_SIGN_NEW;
        this.opinf = 0;
        this.opaddr = 0;
        this.addrMode = 0;
        this.addr = 0;
        this.palCnt = 0;
        this.palEmu = Globals.palEmulation;
        this.emulateSound = Globals.enableSound;
        this.asApplet = Globals.appletMode;
        this.stopRunning = false;
    }

    private int load(int addr)
    {
        if (addr >= 0x2000)
        {
            return this.mmap.load(addr);
        }
        return this.mem[addr & 0x7ff];
    }

    private int load16bit(int addr)
    {
        if (addr >= 0x1fff)
        {
            return (this.mmap.load(addr) | (this.mmap.load(addr + 1) << 8));
        }
        return (this.mem[addr & 0x7ff] | (this.mem[(addr + 1) & 0x7ff] << 8));
    }

    public bool pageCrossed(int addr1, int addr2)
    {
        return ((addr1 & 0xff00) != (addr2 & 0xff00));
    }

    public short pull()
    {
        this.REG_SP++;
        this.REG_SP = 0x100 | (this.REG_SP & 0xff);
        return this.mmap.load(this.REG_SP);
    }

    public void push(int value_Renamed)
    {
        this.mmap.write(this.REG_SP, (short) value_Renamed);
        this.REG_SP--;
        this.REG_SP = 0x100 | (this.REG_SP & 0xff);
    }

    public void requestIrq(int type)
    {
        if (!this.irqRequested || (type != 0))
        {
            this.irqRequested = true;
            this.irqType = type;
        }
    }

    public void reset()
    {
        this.REG_ACC_NEW = 0;
        this.REG_X_NEW = 0;
        this.REG_Y_NEW = 0;
        this.irqRequested = false;
        this.irqType = 0;
        this.REG_SP = 0x1ff;
        this.REG_PC_NEW = 0x7fff;
        this.REG_STATUS_NEW = 40;
        this.Status = 40;
        this.crash = false;
        this.F_CARRY_NEW = 0;
        this.F_DECIMAL_NEW = 0;
        this.F_INTERRUPT_NEW = 1;
        this.F_OVERFLOW_NEW = 0;
        this.F_SIGN_NEW = 0;
        this.F_ZERO_NEW = 0;
        this.F_NOTUSED_NEW = 1;
        this.F_BRK_NEW = 1;
        this.cyclesToHalt = 0;
    }

    public void Run()
    {
        this.initRun();
        this.emulate();
    }

    public void stackWrap()
    {
        this.REG_SP = 0x100 | (this.REG_SP & 0xff);
    }

    public void stateLoad(ByteBuffer buf)
    {
        if (buf.readByte() == 1)
        {
            this.Status = buf.readInt();
            this.REG_ACC_NEW = buf.readInt();
            this.REG_PC_NEW = buf.readInt();
            this.REG_SP = buf.readInt();
            this.REG_X_NEW = buf.readInt();
            this.REG_Y_NEW = buf.readInt();
            this.cyclesToHalt = buf.readInt();
        }
    }

    public void stateSave(ByteBuffer buf)
    {
        buf.putByte(1);
        buf.putInt(this.Status);
        buf.putInt(this.REG_ACC_NEW);
        buf.putInt(this.REG_PC_NEW);
        buf.putInt(this.REG_SP);
        buf.putInt(this.REG_X_NEW);
        buf.putInt(this.REG_Y_NEW);
        buf.putInt(this.cyclesToHalt);
    }

    private void write(int addr, short val)
    {
        if (addr < 0x2000)
        {
            this.mem[addr & 0x7ff] = val;
        }
        else
        {
            this.mmap.write(addr, val);
        }
    }

    public bool Crashed
    {
        set
        {
            this.crash = value;
        }
    }

    public MemoryMapper Mapper
    {
        set
        {
            this.mmap = value;
        }
    }

    private int Status
    {
        get
        {
            return (((((((this.F_CARRY_NEW | (this.F_ZERO_NEW << 1)) | (this.F_INTERRUPT_NEW << 2)) | (this.F_DECIMAL_NEW << 3)) | (this.F_BRK_NEW << 4)) | (this.F_NOTUSED_NEW << 5)) | (this.F_OVERFLOW_NEW << 6)) | (this.F_SIGN_NEW << 7));
        }
        set
        {
            this.F_CARRY_NEW = value & 1;
            this.F_ZERO_NEW = (value >> 1) & 1;
            this.F_INTERRUPT_NEW = (value >> 2) & 1;
            this.F_DECIMAL_NEW = (value >> 3) & 1;
            this.F_BRK_NEW = (value >> 4) & 1;
            this.F_NOTUSED_NEW = (value >> 5) & 1;
            this.F_OVERFLOW_NEW = (value >> 6) & 1;
            this.F_SIGN_NEW = (value >> 7) & 1;
        }
    }
}

